# THIS FILE IS A PART OF VCStudio
# PYTHON 3

import os
import datetime
import json
from subprocess import *

# GTK module ( Graphical interface
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import Gdk
import cairo

# Own modules
from settings import settings
from settings import talk
from settings import fileformats
from settings import oscalls
from project_manager import pm_project

#UI modules
from UI import UI_elements
from UI import UI_color
from UI import UI_math

# Studio
from studio import studio_dialogs
from studio import analytics
from studio import story

# Network / Rendering
from network import network_renders

def save_settings(win, filename):
    
    ############################################################################
    
    # This function will save the render settings file. 
    
    ############################################################################    
    
    folder = filename[:filename.rfind("/")]+"/extra"
    savefile = folder+filename[filename.rfind("/"):]+".json"
    
    # First let's make sure that the extra folder exists. 
    
    try:
        os.makedirs(win.project+folder)
    except:
        pass
    
    # Then let's write the file in there.
    
    with open(win.project+savefile, 'w') as fp:
        json.dump(win.renders[filename], fp, sort_keys=True, indent=4)


def layer(win, call):
    
    ##########################################################################
    
    # This file will setup and manage rendering of shots. It's a bit complex
    # in function. I have 2 network scripts at the moment. And it might grow
    # beyond that.
    
    # See:
    #     network/during_render.py
    #     network/network_renders.py
    
    # This file is the UI part of the process. ( The maing UI ) Some beats and
    # peaces will exists in various windows through out the program.
    
    ##########################################################################
    
    
    # Making the layer
    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, win.current['w'],
                                                      win.current['h'])
    layer = cairo.Context(surface)
    
    
    #text setting
    layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
    
    UI_color.set(layer, win, "dark_overdrop")
    layer.rectangle(
        0,
        0,
        win.current["w"],
        win.current["h"],
        )
    layer.fill()
    
    
    
    UI_color.set(layer, win, "node_background")
    UI_elements.roundrect(layer, win, 
        win.current["w"]/2-250,
        100,
        500,
        win.current["h"]-200,
        10)
    
    # Documentation entry
    def do():
        def after(win, var):
           pass
        
        studio_dialogs.help(win, "help", after, SEARCH=talk.text("documentation_render"))
    
    UI_elements.roundrect(layer, win,
        win.current["w"]/2-250,
        win.current["h"]-140, 
        40,
        40,
        10,
        do,
        "question")
    
    is_rendering = False # This will determen whether 
    
    for render in win.renders:
        if win.renders[render]["rendering"]:
            is_rendering = True
    
    
    if not is_rendering:
        # Render button
        def do():
            
            # This here will launch a script that will be on it's on from now
            # on. See:
            #           network/during_render.py
            
            Popen(["python3", "network/during_render.py", win.project, oscalls.get_current_blender(win)])
            
            
        UI_elements.roundrect(layer, win, 
            win.current["w"]/2-20,
            win.current["h"]-140,
            40,
            40,
            10,
            button=do,
            icon="right")
    else:
        # Stop Render button
        
        def do():
            
            network_renders.stop_render(win)
            
            
        UI_elements.roundrect(layer, win, 
            win.current["w"]/2-20,
            win.current["h"]-140,
            40,
            40,
            10,
            button=do,
            icon="stop")
        
    # Exit button
    def do():
        win.current["calls"][call]["var"] = False 
        
        
    UI_elements.roundrect(layer, win, 
        win.current["w"]/2+210,
        win.current["h"]-140,
        40,
        40,
        10,
        button=do,
        icon="cancel",
        tip=talk.text("cancel"),
        url="render")
    
    
    x = win.current["w"]/2-250 + 10
    y = 100 + 10
    width = 500 - 20
    height = win.current["h"]-200 - 20
    
    UI_elements.roundrect(layer, win, 
        x,
        y,
        width,
        height-60,
        10,
        fill=False)
    layer.clip()
    
    clip = [
        x,
        y,
        width,
        height-60]
    
    
    
    
    # Let's get the filename of the current file that we want to setup.
    filename = win.current["renders_window"]["filename"]
    
    # So this window could be accessed from both main window and from the script.
    # One is to see where each render is. And ther other is to configure and add
    # renders to the list.
    
    if filename:
        
        # Basically if any file is inputted. It means that it's to configure.
        # but sometimes. ( I'm tyring to see myself using it ) the user will
        # click on the render button to just access the render. And to hell with
        # it. Let's make the filename variable the selection of the file.
        
        if filename not in win.renders:
            
            # So on the initialization we want to do a couple of things.
            # First of which will be to get render settings data from the
            # blend file.
            
            # I think a quick bpy script could do.
            # See:
            #     studio/bpy_get_render_settings.py
            
            blenderpath = oscalls.get_current_blender(win)
            blend = win.project+filename
            
            checkframes = Popen([blenderpath, "-b", blend , "-P",
                 os.getcwd()+"/studio/bpy_get_render_settings.py"],stdout=PIPE, universal_newlines=True)
            checkframes.wait()
            checkstring = checkframes.stdout.read()
            
            # We are going to need the following options.
            
            start_frame  = 0
            end_frame    = 250
            image_format = "PNG"
            save_folder  = "storyboard"
            
            
            for line in checkstring.split("\n"):
                
                if line.startswith("Start_frame"):
                    try:
                        start_frame = int(line[line.find(":")+1:])
                    except:
                        pass
                
                if line.startswith("End_frame"):
                    try:
                        end_frame = int(line[line.find(":")+1:])
                    except:
                        pass
            
            # Now since we've got the data. Let's write it to the dictionary first.
            
            win.renders[filename] = {
                "start_frame"  :start_frame , # The frame we want to start on
                "end_frame"    :end_frame   , # The frame we want to end on
                "image_format" :image_format, # What format to save the images
                "save_folder"  :save_folder , # Into what folder to save images
                "clean_folder" :False       , # Whether to delete current frames before rendering
                "current_frame":0           , # What is current frame rendering
                "rendering"    :False       , # Whether it's currently rendering
                "analytics"    :{}            # Times of each frame
                }
           
            # Now in order not to loose the data immediatly. We are going to need
            # to add the filename into a special file. 
            
            s = open(win.project+"/set/active_renders.data", "a")
            s.write(filename+"\n")
            s.close()
            
            # Also we want to make a little json file in the extra folder of
            # the shot. This will contain our settings. And the file will be
            # read by the renderer script while it's running. It has to be on
            # it's own. So it's good to have extendable files.
            
            save_settings(win, filename)
            
    # Now let's get to the actuall UI of the stuff. Basically I want it to
    # always give us a list of the currently set renders. And one of them 
    # might be selected and expendet to see they settings / current data.
            
    # Setting up the scroll
    if "render" not in win.scroll:
        win.scroll["render"] = 0
    
    current_Y = 0
    
    # So let's do this. 
    
    is_rendering = False # This will determen whether 
    
    # Before we dive into settings and graphs. Let's make a deletion
    if 65535 in win.current["keys"] and not win.renders[win.current["renders_window"]["filename"]]["rendering"]:
        try:
            del win.renders[win.current["renders_window"]["filename"]]
            
            active_renders = open(win.project+"/set/active_renders.data")
            active_renders = active_renders.read()
            active_renders = active_renders.split("\n")
            
            s = open(win.project+"/set/active_renders.data", "w")
            
            for i in active_renders:
                if i != win.current["renders_window"]["filename"] and i:
                    s.write(i+"\n")
            
            s.close()
        
        except:
            pass
        win.current["renders_window"]["filename"] = ""
        win.current["keys"] = []
    
    for render in win.renders:
        
        # Let's get a shot name for each render.
        
        shot = render[:render.rfind("/")].replace("/rnd", "")
        blend = render[render.rfind("/"):]
        
        tip = (shot+blend).replace("/", "", 1).replace("/", " | ")
        
        if win.renders[render]["rendering"]:
            tip = win.renders[render]["rendering"]
        
        def do():
            win.current["renders_window"]["filename"] = render
        
        UI_elements.roundrect(layer, win,
            x,
            y+current_Y+win.scroll["render"], 
            width,
            80,
            10,
            button=do,
            tip=tip,
            clip=clip)
        
        # I think the render logo could be cool.
        
        UI_elements.image(layer, win, 
            "settings/themes/"+win.settings["Theme"]+"/icons/render.png",
            x+5, y+current_Y+win.scroll["render"]+5, 40, 40)
        
        # And the name of the shot
        
        UI_color.set(layer, win, "text_normal")
        layer.set_font_size(20)
        layer.move_to(x+60,
                y+current_Y + win.scroll["render"]+30)
        layer.show_text((shot+blend).replace("/", "", 1).replace("/", " | "))
        
        # And let's draw the fraction
        
        fraction =  (win.renders[render]["current_frame"] - win.renders[render]["start_frame"])\
                  / (win.renders[render]["end_frame"] - win.renders[render]["start_frame"])
        
        fraction = min(1, fraction)
        
        UI_color.set(layer, win, "progress_background")
        UI_elements.roundrect(layer, win,
            x+20,
            y+current_Y + win.scroll["render"]+55, 
            width-40,
            0,
            5)

        UI_color.set(layer, win, "progress_active")
        UI_elements.roundrect(layer, win,
            x+20,
            y+current_Y + win.scroll["render"]+55, 
            (width-40)*fraction,
            0,
            5)
        
        # Now selection. When you click on one you expend it. And you can see
        # what settings are inside.
        
        if win.current["renders_window"]["filename"] == render:
         
            UI_color.set(layer, win, "progress_background")
            UI_elements.roundrect(layer, win,
                x,
                y+current_Y+win.scroll["render"], 
                width,
                80,
                10,
                fill=False)
            layer.stroke()
            
            current_Y = current_Y + 90
            
            
            
            # We are going to have 2 differnt UI's for those who are rendering 
            # and those who are not rendering.
            
            if not win.renders[render]["rendering"]:
                
                # We need to choose the folder of where the given render will be done.
                # For the user tho I don't think nessesary to understand that it's a
                # folder nessesary. They will see 4 circles. 4 render qualities. And
                # will decide stuff based on that.
                
                fouricons = [ "storyboard", "opengl", "test_rnd", "rendered"]
                
                for num, f in enumerate(fouricons):
                    if f == win.renders[render]["save_folder"]:
                        
                        UI_color.set(layer, win, "progress_time")
                        UI_elements.roundrect(layer, win,
                            x+20+(40*num),
                            y+current_Y + win.scroll["render"], 
                            40,
                            40,
                            10)
                    
                    def do():
                        win.renders[render]["save_folder"] = f
                        save_settings(win, render)
                        
                    UI_elements.roundrect(layer, win,
                        x+20+(40*num),
                        y+current_Y + win.scroll["render"], 
                        40,
                        40,
                        10,
                        button=do,
                        icon=f,
                        tip=f)
                
                # Here also I want to have a little clean icon. This will make sure
                # to clean the current frames that are currently inside the folder.
                # I know it's slightly counter intuitive compared to the rest of
                # the program in that it's better not to give the user the ability
                # to delete any actuall files. But in this case it makes a bit
                # sense. Since for the user rendering again is like replacing the
                # previous files with new ones. And the algorythm always renders
                # from the last frame in the folder. So it never deletes anything
                # by defaul. So I guess a button to clean frames could be a good
                # thing.
                
                if win.renders[render]["clean_folder"]:
                
                    UI_color.set(layer, win, "progress_time")
                    UI_elements.roundrect(layer, win,
                        x+width-40,
                        y+current_Y + win.scroll["render"], 
                        40,
                        40,
                        10)
                
                def do():
                    win.renders[render]["clean_folder"] = not win.renders[render]["clean_folder"]
                    save_settings(win, render)
                    
                UI_elements.roundrect(layer, win,
                    x+width-40,
                    y+current_Y + win.scroll["render"], 
                    40,
                    40,
                    10,
                    button=do,
                    icon="clean",
                    tip=talk.text("clean_render_folder"))
                
                        
                current_Y = current_Y + 50
                
                
                # Now let's put the settings them selves.
                # First thing is we need start and end frames. And we also need it
                # somehow readable for the user.
                
                # I will probably need this.
                def is_number(string):
                    try:
                        int(string)
                        return True
                    except:
                        return False
                        
                # START FRAME
                
                UI_elements.text(layer, win, render+"start_frame",
                    x+10,
                    y+current_Y+win.scroll["render"],
                    100,
                    40,
                    set_text=str(win.renders[render]["start_frame"]),
                    tip=talk.text("rendering_start_frame"))
                
                if win.text[render+"start_frame"]["text"] != str(win.renders[render]["start_frame"])\
                and is_number(win.text[render+"start_frame"]["text"]):
                    def do():
                        win.renders[render]["start_frame"] = int(win.text[render+"start_frame"]["text"])
                        save_settings(win, render)
                        
                        
                    UI_elements.roundrect(layer, win, 
                        x+70,
                        y+current_Y+win.scroll["render"],
                        40,
                        40,
                        10,
                        button=do,
                        icon="ok",
                        tip=talk.text("checked"))
                
                
                
                
                # FILE FORMATS
                
                # I will not add any video files since the algoryhm will require 
                # actuall frames to be stored as separate files. This will insure 
                # that ALL frames were rendered. And in case of crash the algorythm
                # will pick up from a previous frame in a folder. With video is not
                # only impossible. But in case of a crash with video all the previous
                # frames will be lost.
                
                # What I want to do is to ejucate the user on the Open Formats.
                # You know using OGG video instead of MP4 and stuff like that.
                # For images there are the same types of Open Formats. The point
                # is that these formats could be made and played using entirelly
                # free software. 
                
                # I will let selection of formats that are not in the list but I
                # will mark them as non-recommended. I will do it like so.
                
                #  [ <start frame> ] [ PNG ] [ <png frame>
                
                            #   V     PNG      ?
                            #   V     JPEG     ?
                            #   V     EXR      ?
                            #   X     HDR      ?
                
                # And so on and forth. You can see that HDR is marked with an X
                # it will be a button linking to the :
                linfo = "http://www.linfo.org/free_file_format.html"
                # so the user could read a full document describing desisions
                # about the formats.
                
                
                
                formats = {
                    "PNG" : [True, "Image PNG",                     "https://en.wikipedia.org/wiki/Portable_Network_Graphics"],
                    "JPEG": [True, "Image JPEG",                    "https://en.wikipedia.org/wiki/JPEG"],
                    "EXR" : [True, "Open EXR",                      "https://en.wikipedia.org/wiki/OpenEXR"],
                    "HDR" : [False,"Radiance HDR",                  "https://en.wikipedia.org/wiki/RGBE_image_format"],
                    "BMP" : [False,"Microsoft BMP",                 "https://en.wikipedia.org/wiki/BMP_file_format"],
                    "TGA" : [False,"Truevision TGA",                "https://en.wikipedia.org/wiki/Truevision_TGA"],
                    "TIFF": [False,"Tiff",  "https://en.wikipedia.org/wiki/Tagged_Image_File_Format"]
                     }
                
                if "selecting_render_file_format" not in win.current:
                    win.current["selecting_render_file_format"] = False
                
                def do():
                    win.current["selecting_render_file_format"] = not win.current["selecting_render_file_format"]
                    
                UI_elements.roundrect(layer, win,
                    x+120,
                    y+current_Y + win.scroll["render"], 
                    235,
                    40,
                    10,
                    button=do,
                    tip=talk.text("rendering_file_format"))
                
                currentformat = win.renders[render]["image_format"]
                
                
                if not win.current["selecting_render_file_format"]:
                    UI_color.set(layer, win, "text_normal")
                    layer.set_font_size(20)
                    layer.move_to(win.current['w']/2-len(formats[currentformat][1])*6,
                            y+current_Y + win.scroll["render"]+30)
                    layer.show_text(formats[currentformat][1])
                
                
                
                # END FRAME
                UI_elements.text(layer, win, render+"end_frame",
                    x+365,
                    y+current_Y+win.scroll["render"],
                    100,
                    40,
                    set_text=str(win.renders[render]["end_frame"]),
                    tip=talk.text("rendering_end_frame"))
                
                if win.text[render+"end_frame"]["text"] != str(win.renders[render]["end_frame"])\
                and is_number(win.text[render+"end_frame"]["text"]):
                    def do():
                        win.renders[render]["end_frame"] = int(win.text[render+"end_frame"]["text"])
                        save_settings(win, render)
                        
                    UI_elements.roundrect(layer, win, 
                        x+215+210,
                        y+current_Y+win.scroll["render"],
                        40,
                        40,
                        10,
                        button=do,
                        icon="ok",
                        tip=talk.text("checked"))
                
                
                current_Y = current_Y + 50
                
                
                
                if win.current["selecting_render_file_format"]:
                    
                    for num, f in enumerate(formats):
                        
                        if f == win.renders[render]["image_format"]:
                            
                            UI_color.set(layer, win, "progress_time")
                            UI_elements.roundrect(layer, win,
                                x+120,
                                y+current_Y + win.scroll["render"], 
                                235,
                                40,
                                10)
                            
                            
                        def do():
                            win.renders[render]["image_format"] = f
                            save_settings(win, render)
                            win.current["selecting_render_file_format"] = False
                            
                        UI_elements.roundrect(layer, win,
                            x+120,
                            y+current_Y + win.scroll["render"], 
                            235,
                            40,
                            10,
                            button=do)
                            
                        UI_color.set(layer, win, "text_normal")
                        layer.set_font_size(20)
                        layer.move_to(win.current['w']/2-len(formats[f][1])*6,
                                y+current_Y + win.scroll["render"]+30)
                        layer.show_text(formats[f][1])
                        
                        # RECCOMENDATION
                        
                        if formats[f][0]:
                            rec = talk.text("recommended_yes")
                            ic  = "ok"
                        else:
                            rec = talk.text("recommended_not")
                            ic  = "cancel"
                        
                        def do():
                            os.system("xdg-open "+linfo)
                            
                        UI_elements.roundrect(layer, win,
                            x+10,
                            y+current_Y + win.scroll["render"], 
                            40,
                            40,
                            10,
                            button=do,
                            icon=ic,
                            tip=rec)
                        
                        # WIKIPEDIA ABOUT THE FORMAT
                        
                        def do():
                            os.system("xdg-open "+formats[f][-1])
                            
                        UI_elements.roundrect(layer, win,
                            x+430,
                            y+current_Y + win.scroll["render"], 
                            40,
                            40,
                            10,
                            button=do,
                            icon="question",
                            tip="Wikipedia")
                        
                        current_Y = current_Y + 50
                        
            else:
                
                # And here comes the UI of when it's during RENDERING
                
                # First thing will be to draw a little graph. This will show current
                # frame and analytics data about render times.
                
                UI_color.set(layer, win, "dark_overdrop")
                layer.rectangle(
                    x+5,
                    y+current_Y+win.scroll["render"], 
                    width-10,
                    100)
                layer.fill()
                
                for frame in range(win.renders[render]["start_frame"], win.renders[render]["end_frame"]+1):
                    
                    numofis = win.renders[render]["end_frame"] - win.renders[render]["start_frame"]
                    
                    if frame == win.renders[render]["current_frame"]:
                    
                        UI_color.set(layer, win, "progress_time")
                        layer.rectangle(
                            x+5+(width-10)/numofis*(frame-1),
                            y+current_Y+win.scroll["render"], 
                            (width-10)/numofis,
                            100)
                        layer.fill()
                    
                    # Now I want to be able to interact with the graph. For this I want to 
                    # add a little mouse sensitive region. That will give me data about the
                    # current frame. In a tooltip?
                    
                    if int(win.current["mx"]) in range(int(x+5+(width-10)/numofis*frame),int(x+5+(width-10)/numofis*frame+(width-10)/numofis))\
                    and int(win.current["my"]) in range(int(y+current_Y+win.scroll["render"]), int(y+current_Y+win.scroll["render"]+100)):
                        
                        UI_color.set(layer, win, "progress_background")
                        layer.move_to(x+5+(width-10)/numofis*(frame-0.5),
                            y+current_Y+win.scroll["render"]
                            )
                        layer.line_to(
                            x+5+(width-10)/numofis*(frame-0.5),
                            y+current_Y+win.scroll["render"]+100
                            )
                        layer.stroke()
                        if win.renders[render]["save_folder"] in win.renders[render]["analytics"]:
                            if str(frame) in win.renders[render]["analytics"][win.renders[render]["save_folder"]]:
                            
                                value = win.renders[render]["analytics"][win.renders[render]["save_folder"]][str(frame)]
                            
                                UI_elements.tooltip(win, UI_math.timestring(value/1000000))
                        
                # Now let's draw a graph. I love graphs. They are cool AF. First of all tho
                # we need to know the maximum value. Because who know how long was the render
                
                mx = 0
                allvalues = []
                if win.renders[render]["save_folder"] in win.renders[render]["analytics"]:
                    for v in win.renders[render]["analytics"][win.renders[render]["save_folder"]]:
                        mx = max(win.renders[render]["analytics"][win.renders[render]["save_folder"]][v], mx)
                        allvalues.append(win.renders[render]["analytics"][win.renders[render]["save_folder"]][v])
                        
                UI_color.set(layer, win, "progress_background")
                layer.move_to(x+5, y+current_Y+win.scroll["render"]+100)
                for frame in range(win.renders[render]["start_frame"], win.renders[render]["end_frame"]+1):
                    numofis = win.renders[render]["end_frame"] - win.renders[render]["start_frame"]
                    if win.renders[render]["save_folder"] in win.renders[render]["analytics"]:
                        if str(frame) in win.renders[render]["analytics"][win.renders[render]["save_folder"]]:
                            
                            value = win.renders[render]["analytics"][win.renders[render]["save_folder"]][str(frame)]
                            
                            layer.line_to(
                                x+5+(width-10)/numofis*(frame-0.5),
                                (y+current_Y+win.scroll["render"]+100)-(100/mx*value)
                                )
                layer.stroke()
                        
                current_Y = current_Y + 110
                
                # Now let's put out some data in the text format. For the user to
                # know stuff he or she might want to know.
                
                # AVARAGE TIME
                
                avarage = 0
                try:
                    avarage = sum(allvalues) / len(allvalues)
                except:
                    pass
                
                UI_color.set(layer, win, "text_normal")
                layer.set_font_size(20)
                layer.move_to(x+10,
                        y+current_Y + win.scroll["render"]+30)
                layer.show_text(talk.text("render_avarage_time")+" "+UI_math.timestring(avarage/1000000))
                
                current_Y = current_Y + 40
                
                # REMAINING TIME
                
                remaining = avarage * (win.renders[render]["end_frame"] - win.renders[render]["current_frame"])
                
                UI_color.set(layer, win, "text_normal")
                layer.set_font_size(20)
                layer.move_to(x+10,
                        y+current_Y + win.scroll["render"]+30)
                layer.show_text(talk.text("render_remaining_time")+" "+UI_math.timestring(remaining/1000000))
                
                current_Y = current_Y + 40
                
                
        else:
        
            current_Y = current_Y + 85
    
    ###########################
        
    UI_elements.scroll_area(layer, win, "render", 
        x,
        y,
        width,
        height-60,
        current_Y,
        bar=True,
        mmb=True,
        url="render"
    )
    
    
    return surface
